package com.platform.comm.ftp;


import com.platform.comm.ftp.exception.FtpException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.net.PrintCommandListener;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;

import java.io.*;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class DgFTPClientImpl extends FTPClient implements DgFTPClient {

    private String hostname;

    private int port;

    private String username;

    private String password;

    public DgFTPClientImpl(String hostname) {
        super();
        this.hostname = hostname;
        this.port = 21;
        this.username = "anonymous";
        this.password = null;
    }

    public DgFTPClientImpl(String hostname, String username, String password) {
        super();
        this.hostname = hostname;
        this.port = 21;
        this.username = username;
        this.password = password;
    }

    public DgFTPClientImpl(String hostname, int port, String username, String password) {
        super();
        this.hostname = hostname;
        this.port = port;
        this.username = username;
        this.password = password;
    }

    /**
     *  验证ftp是否可连接
     * @return
     */
    public boolean tryConnect() {
        boolean result = false;
        try {
            this.setConnectTimeout(2000);
            this.connect(this.hostname,this.port);
            if(FTPReply.isPositiveCompletion(this.getReplyCode())) {
                if(this.login(this.username,this.password)) {
                    result = true;
                }
            }
            this.disconnect();
        } catch (SocketException e) {
            log.error("ftp try connect is error:{}",e);
        } catch (IOException e) {
            log.error("ftp try connect is error:{}",e);
        }
        return result;
    }

    /**
     *  列出目录下的文件
     * @param path
     * @return
     */
    public Map<String,FTPFile> listFile(String path) {
        if(path == null || path.length() == 0) {
            path = "/";
        }
        login();
        return getFileList(new HashMap<>(), path , null);
    }

    /**
     *  下载文件
     * @param sourceDir 源文件目录
     * @param sourceFile 源文件
     * @param descFile 下载文件存放地址
     * @return
     */
    public File download(String sourceDir, String sourceFile, String descFile) {
        if(sourceDir == null || sourceDir.trim().length() == 0) {
            sourceDir = "/";
        }
        if(sourceFile == null || sourceFile.trim().length() == 0) {
            throw new RuntimeException("fileName文件名不能为空");
        }
        if(descFile == null || descFile.trim().length() == 0) {
            throw new RuntimeException("descFilePath文件存放地址不能为空");
        }
        FileOutputStream fos = null;
        File df = null;
        try {
            // 建立ftp连接
            login();
            if(!checkFileExist(sourceDir,sourceFile)) {
                throw new FtpException(FtpException.ErrorCode.FILE_NOT_FOUND);
            }
            if(changeWorkingDirectory(sourceDir)) {
                // 文件存在则删除
                df = new File(descFile);
                if(df.exists()){
                    df.delete();
                } if(!df.getParentFile().exists()) {
                    df.getParentFile().mkdirs();
                }
                // 目的文件输出流
                fos = new FileOutputStream(df);
                // 下载文件
                if(!retrieveFile(sourceFile,fos)) {
                    if(df.exists()){
                        df.delete();
                    }
                    throw new FtpException(FtpException.ErrorCode.FILE_NOT_FOUND);
                }
            } else {
                throw new FtpException(FtpException.ErrorCode.DOWNLOAD_FAILED);
            }
        } catch (Exception e) {
            log.error("ftp list file is error:{}",e);
            throw new FtpException(FtpException.ErrorCode.DOWNLOAD_FAILED);
        } finally {
            try {
                if(isConnected()) {
                    disconnect();
                }
                if(fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                log.error("ftp download close OutputStream is error:{}",e);
            }
        }
        return df;
    }

    /**
     *  上传文件
     * @param sourceFile 所要上传文件路径
     * @param descDir 上传ftp服务器地址
     * @param descFile 上传文件名
     * @return
     */
    public void upload(String sourceFile, String descDir, String descFile) {
        FileInputStream fis = null;
        try {
            login();
            // 切换目录
            if(changeWorkingDirectory(descDir)) {
                fis = new FileInputStream(sourceFile);
                if(!storeFile(new String(descFile.getBytes(StandardCharsets.UTF_8),StandardCharsets.UTF_8), fis)) {
                    throw new FtpException(FtpException.ErrorCode.UPLOAD_FAILED);
                }
            } else {
                throw new FtpException(FtpException.ErrorCode.FILE_NOT_FOUND);
            }
        } catch (IOException e) {
            log.error("ftp upload file is error:{}",e);
            throw new FtpException(FtpException.ErrorCode.FILE_NOT_FOUND);
        } finally {
            try {
                if(isConnected()) {
                    disconnect();
                }
                if(fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                log.error("ftp upload file close stream is error:{}",e);
            }
        }
    }

    /**
     *  删除文件
     * @param descDir 所要删除文件路径
     * @param descFile 所要删除文件名
     * @return
     */
    public void delete(String descDir, String descFile) {
        try {
            login();
            // 切换目录
            if(changeWorkingDirectory(descDir)) {
                if(!deleteFile(new String(descFile.getBytes(StandardCharsets.UTF_8),StandardCharsets.UTF_8))) {
                    throw new FtpException(FtpException.ErrorCode.DELETE_FAILED);
                }
            } else {
                throw new FtpException(FtpException.ErrorCode.FILE_NOT_FOUND);
            }
        } catch (IOException e) {
            log.error("ftp upload file is error:{}",e);
            throw new FtpException(FtpException.ErrorCode.DELETE_FAILED);
        } finally {
            try {
                if(isConnected()) {
                    disconnect();
                }
            } catch (IOException e) {
                log.error("ftp delete file disconnect is error:{}",e);
            }
        }
    }


    private boolean checkFileExist(String descDir, String descFile) {
        boolean result = false;
        try {
            return MapUtils.isNotEmpty(getFileList(new HashMap<>(), descDir , descFile));
        } catch (Exception e) {
            log.error("ftp check file exist is error:{}",e);
        }
        return result;
    }

    /**
     *  连接登录
     * @return
     */
    private void login() {
        try {
            // 建立连接
            connect(this.hostname,this.port);
            if(log.isDebugEnabled()) {
                addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));
            }
            // 登录
            if(login(this.username,this.password)) {
                // 设置传输文件的类型
                this.setFileType(BINARY_FILE_TYPE);
                // 设置控制连接编码
                this.setControlEncoding("UTF-8");
                // 切换被动模式
                enterLocalPassiveMode();
                // 支持utf-8
                sendCommand("OPTS UTF8", "ON");
            } else {
                throw new FtpException(FtpException.ErrorCode.LOGIN_ERROR);
            }
        } catch (IOException e) {
            log.error("ftp login is error:{}",e);
            if(isConnected()) {
                try {
                    disconnect();
                } catch (IOException ex) {
                    log.error("disconnect is error:{}",e);
                }
            }
            throw new FtpException(FtpException.ErrorCode.LOGIN_ERROR);
        }
    }

    /**
     *  不支持中文路径
     * @param map
     * @param path
     * @param filterName
     * @return
     */
    private Map<String,FTPFile> getFileList(Map<String,FTPFile> map, String path , String filterName) {
        try {
            Arrays.stream(listFiles(new String(path.getBytes(StandardCharsets.UTF_8),StandardCharsets.UTF_8))).forEach(ftpFile -> {
                if(!".".equalsIgnoreCase(ftpFile.getName()) && !"..".equalsIgnoreCase(ftpFile.getName())) {
                    String dir = new String((path + File.separator + ftpFile.getName()).getBytes(StandardCharsets.UTF_8),StandardCharsets.UTF_8);
                    if(filterName == null || filterName.trim().length() == 0) {
                        map.put(dir,ftpFile);
                    } else if (filterName.equalsIgnoreCase(ftpFile.getName())) {
                        map.put(dir, ftpFile);
                    }
                    if(ftpFile.isDirectory()) {
                        getFileList(map, dir , filterName);
                    }
                }
            });
        } catch (IOException e) {
            log.error("ftp list file is error:{}",e);
        }
        return map;
    }
}
